#!/usr/bin/env python
import sys
import time
import math

import direct.directbase.DirectStart
from direct.task import Task
from direct.actor import Actor
from direct.showbase import DirectObject
from direct.gui.OnscreenText import OnscreenText
from direct.fsm import FSM

from direct.interval.IntervalGlobal import *

import control

#~ import psyco
#~ psyco.full()

from level import *
from screens import *

import config

class Game(FSM.FSM):
    def __init__(self):
        FSM.FSM.__init__(self, 'Game')

        self.defaultTransitions = {
            'Title' : [ 'Options', 'LevelSelect', 'Exit' ],
            'Options' : [ 'Title' ],
            'LevelSelect' : [ 'Level', 'Title'],
            'Level' : [ 'Result' ],
            'Result' : [ 'Level', 'LevelSelect', 'Title'],
            }

        self.options = config.MoonBunnyConfig(user_file='config.cfg',
                                              defaults_file='default.cfg')
        self.hiscores = config.MoonBunnyConfig('hiscores.cfg')

        self.start_level_sfx = loader.loadSfx('./sound/start_level.wav')
        self.theme = loader.loadMusic('./sound/always.wav')
        self.theme.setLoop(True)
        
        logo_sound= loader.loadSfx('./sound/elefante.wav')
        
        self.logo = OnscreenImage(image='./image/tromba_logo.png', scale=(512.0/base.win.getXSize(), 1 ,256.0/base.win.getYSize()), pos = (0.0, 2.0, 0.0), parent=render2d)
        self.logo.setTransparency(TransparencyAttrib.MAlpha)
        Sequence(
            LerpFunc(self.logo.setAlphaScale, fromData=.0, toData=1, duration=.5),
            SoundInterval(logo_sound, duration=4.0),
            LerpFunc(self.logo.setAlphaScale, fromData=1, toData=.0, duration=.5),
            Func(self.request,'Title')
            ).start()
    
    ## Title state
    def enterTitle(self):
        self.logo.destroy()
        if self.theme.status() == 1:
            self.theme.play()
        self.title_screen = TitleScreen()
        
    def exitTitle(self):
        self.title_screen.clear()

    def filterTitle(self, request, args):
        if request == 'nav-up' or request == 'nav-down':
            self.title_screen.option_changed(request.replace('nav-', ''))
            
        if request == 'nav-confirm':
            if self.title_screen.option_pressed() == 'start':
                return 'LevelSelect'
            if self.title_screen.option_pressed() == 'options':
                return 'Options'
            if self.title_screen.option_pressed() == 'exit':
                return 'Exit'
            
        if request == 'nav-back':
            return 'Exit'

    ## Options state
    def enterOptions(self):
        self.options_screen = OptionsScreen(self.options)
        
    def exitOptions(self):
        self.options_screen.clear()
        self.options.save()
        
    def filterOptions(self, request, args):
        if request == 'nav-left' or request == 'nav-right':
            self.options_screen.option_changed(request)
        if request == 'nav-back':
            return 'Title'
        
    ## LevelSelect state
    def enterLevelSelect(self):
        if self.theme.status() == 1:
            self.theme.play()
        self.level_select = LevelSelectScreen(self.hiscores)
        
    def exitLevelSelect(self):
        self.level_select.clear()
        
    def filterLevelSelect(self, request, args):
        if request == 'nav-confirm':
            level_name = self.level_select.option_pressed()
            return ('Level', level_name, 'Normal')
        if request == 'nav-back':
            return 'Title'
        if request == 'nav-left'or request == 'nav-right':
            self.level_select.option_changed(request.replace('nav-', ''))
        
    ## Level state
    def enterLevel(self, level, difficulty):
        self.theme.stop()
        self.level = Level(level, difficulty=difficulty, options=self.options)
        Sequence(SoundInterval(self.start_level_sfx), Func(self.level.setup), Func(self.level.play)).start()
    
    def exitLevel(self):
        self.level_name = self.level.name
        self.level_score = self.level.score
        self.rank_stats = (self.level.judgement_stats, self.level.n_rings)
        del self.level
        self.level = None

    ## Result
    def enterResult(self):
        rank = self.calculate_rank(*self.rank_stats)
        self.save_score(self.level_name, rank, self.level_score)
        self.result_screen = ResultScreen(rank, self.level_score, self.rank_stats[0])
        
    def exitResult(self):
        self.result_screen.clear()

    def filterResult(self, request, args):
        if request == 'nav-confirm':
            return 'LevelSelect'
        if request == 'nav-back':
            return 'LevelSelect'

    def enterExit(self):
        sys.exit(0)

    def save_score(self, levelname, rank, score):
        if not self.hiscores.has_section('hiscores'):
            self.hiscores.add_section('hiscores')
            
        if self.hiscores.has_option('hiscores', levelname):
            old_rank, old_score = self.hiscores.get('hiscores', levelname).split(',')
            
            if not rank == 's':
                if ord(old_rank) < ord(rank): 
                    rank = old_rank
            
            if int(old_score) > score: 
                score = old_score
                    
        self.hiscores.set('hiscores', levelname, "%s,%s" % (rank, str(score)))
        self.hiscores.save()

    def calculate_rank(self, stats, n):
        rates = {
            "PERFECT": float(stats['PERFECT'] / float(n)),
            "GOOD": float(stats['GOOD'] / float(n)),
            "OK" : float(stats['OK'] / float(n)),
            "BAD" : float(stats['BAD'] / float(n)),
            "MISS": float(stats['MISS'] / float(n))
        }
        
        if( rates['MISS'] <= 0 and rates['BAD'] <= 0.1 and rates['PERFECT'] >=0.5):
            rank = 's'
            
        elif( rates['MISS'] <= 0.05 and (rates['MISS'] + rates['BAD'] <= 0.2) and (rates['GOOD'] + rates['PERFECT'] >=0.4) ):
            rank = 'a'
            
        elif( (rates['MISS'] + rates['BAD'] <= 0.3) and (rates['GOOD'] + rates['PERFECT'] >=0.3) ):
            rank = 'b'
            
        elif( (rates['MISS'] + rates['BAD'] <= 0.4) and (rates['GOOD'] + rates['PERFECT'] >=0.2) ):
            rank = 'c'
            
        else:
            rank = 'f'
        
        print rates
        
        return rank

class Controller(DirectObject.DirectObject):
    def __init__(self, game):
        self.game = game
        
        self.accept("nav-up", self.game.request, ['nav-up'])
        self.accept("nav-down", self.game.request, ['nav-down'])
        self.accept("nav-left", self.game.request, ['nav-left'])
        self.accept("nav-right", self.game.request, ['nav-right'])
        self.accept("nav-confirm", self.game.request, ['nav-confirm'])
        self.accept("nav-back", self.game.request, ['nav-back'])
        
        self.accept("level-finished", self.game.request, ['Result'])
    
    
class JoystickManager:
    def __init__(self):
        pygame.init()
        pygame.joystick.init()
        taskMgr.add(self.ctask_CheckEvents, "pygame-event")
        
        print "Joysticks available:", pygame.joystick.get_count()
        
        self.joy_list = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
        
        if self.joy_list:
            pass
    
    def init_joy(self, id):
        self.joy_list[id].init()
    
    def set_current_joy(self, id):
        pass
    
    def ctask_CheckEvents(self, task):
        pygame.event.pump()

        if self.joy.get_button(2): messenger.send("joy-button", ["A"])
        if self.joy.get_button(1): messenger.send("joy-button", ["B"])
        if self.joy.get_button(3): messenger.send("joy-button", ["C"])
        if self.joy.get_button(0): messenger.send("joy-button", ["D"])
            
        return Task.cont
    
#~ class ButtonMap(DirectObject.DirectObject):
    #~ def __init__(self, game, mode="joy", j_id=0):
        #~ self.game = game
        
        #~ map = self.__loadMap('map.key')
        #~ self.joy_map = {}
        
        #~ for command in ['confirm', 'back']:
            #~ self.accept(map[command]['key'], self.game.request, [command])
            #~ self.joy_map[map[command]['joy']] = command
        
        #~ for button in ['btn_A', 'btn_B', 'btn_C', 'btn_D']:
            #~ self.accept(map[command]['key'], self.game.request, [command])
        
        #~ for axe in ['axis_x', 'axis_y']:
            #~ self.accept(map[axe], self.game.request(command))
            #~ self.accept(map[command], self.game.request(command))
        
        #~ self.mode = mode
        
        #~ if mode == "joy":
            #~ self.joy = pygame.joystick.Joystick(j_id)
            #~ self.joy.init()
            
        #~ self.arrow_keys = {
            #~ "left":0,
            #~ "right":0,
            #~ "up":0,
            #~ "down":0
        #~ }
        
        #~ #taskMgr.add(self.ctask_JoyEvent, "joy-event")
    
    #~ def __loadMap(self, file):
        #~ # TODO: carregar de um arquivo
        #~ return {
            #~ 'confirm' : {'key':'space', 'joy':2},
            #~ 'back' : {'key':'escape', 'joy':1},
            
            #~ 'up' : {'key':'arrow_up', 'joy':2},
            #~ 'down' : {'key':'arrow_down', 'joy':2},
            
            #~ 'btn_A' : {'key':'v', 'joy':2},
            #~ 'btn_B' : {'key':'c', 'joy':1},
            #~ 'btn_C' : {'key':'x', 'joy':3},
            #~ 'btn_D' : {'key':'z', 'joy':0},
                
            #~ 'axis_x':{'key':('arrow_left', 'arrow_right'), 'joy': 0},
            #~ 'axis_y':{'key':('arrow_down', 'arrow_up'), 'joy': 1},
        #~ }
    
    #~ def setMode(self, mode):
        #~ self.mode = mode
        
    #~ def __setitem__(self, key, value):
        #~ self.buttons[key] = value
        
    #~ def __getitem__(self, key):
        #~ if self.mode == "key":
            #~ result = self.buttons[key]
        #~ else:
            #~ pygame.event.pump()
            #~ result = {
            #~ "right": (self.joy.get_axis(0) > 0) and 1 else 0,
            #~ "left": (self.joy.get_axis(0) < 0) and 1 else 0,
            #~ "up": 1 if self.joy.get_axis(1) < 0 else 0,
            #~ "down": 1 if self.joy.get_axis(1) > 0 else 0
            #~ }[key]
            
        #~ return result
    
    #def get_axis(self):
        
    
    #~ def ctask_JoyEvent(self, task):
        #~ pygame.event.pump()
        
        #~ if self.joy.get_button(2): messenger.send("joy-button", ["A"])
        #~ if self.joy.get_button(1): messenger.send("joy-button", ["B"])
        #~ if self.joy.get_button(3): messenger.send("joy-button", ["C"])
        #~ if self.joy.get_button(0): messenger.send("joy-button", ["D"])
            
        #~ return Task.cont
            

if __name__ == '__main__':
    #messenger.toggleVerbose()

    base.enableParticles()
    base.disableMouse()
    base.setBackgroundColor(.0, .0, .0, .0)
    
    control.KeyNavMapper()
    
    try:
        pygame.init()
        pygame.joystick.init()
        j = pygame.joystick.Joystick(0)
        j.init()
        control.JoyNavMapper(j).activate()
        
    except pygame.error, e:
        print e
    
    game = Game()
    Controller(game)
    
    run()
